#include "publicFunctions.h"
#include "user_threads.h"
#include "user_mutex.h"
#include "user_io.h"

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define PRINTF_MAX 200
#define PRINTF(...) { \
	char buf[PRINTF_MAX]; \
	snprintf(buf, PRINTF_MAX, __VA_ARGS__); \
	uthread_write(STDOUT_FILENO, buf, strlen(buf)); }

// #define PRINTF printf

uthread_mutex_t mutex, mutex2;

void thread_print(char* str) {
	PRINTF("Thread %3d: %s\n", uthread_self(), str);
}

int failures;

void thread_test(char* str, int result, int expected) {
	char buf[PRINTF_MAX];
	if (result == expected)
		sprintf(buf, "%s -- OK", str);
	else {
		failures++;
		sprintf(buf, "%s -- FAIL (got %d, expected %d)", str, result, expected);
	}
	thread_print(buf);
}

void thread_stats() {
	PRINTF("==========\n");
	PRINTF("Total %d failures\n", failures);
}

#include <time.h>

void wait_sec(int seconds) {
	int start = clock();
	while ((clock() - start) / CLOCKS_PER_SEC < seconds) {
	}
}

int thread(void* arg) {
	uthread_mutex_t mutex;
	// thread_print("created");
	thread_test("creating NULL mutex", uthread_mutex_init(NULL), MUTEX_FAILURE);
	thread_test("creating mutex", uthread_mutex_init(&mutex), MUTEX_SUCCESS);
	thread_test("locking mutex", uthread_mutex_lock(mutex), MUTEX_SUCCESS);
	thread_test("locking mutex once more", uthread_mutex_lock(mutex),
			MUTEX_SUCCESS);
	thread_test("destroying locked mutex", uthread_mutex_destroy(mutex),
			MUTEX_LOCKED);
	thread_test("unlocking mutex", uthread_mutex_unlock(mutex), MUTEX_SUCCESS);
	thread_test("unlocking mutex once more", uthread_mutex_unlock(mutex),
			MUTEX_SUCCESS);
	thread_test("unlocking mutex once more", uthread_mutex_unlock(mutex),
			MUTEX_UNLOCKED);
	thread_test("destroying unlocked mutex", uthread_mutex_destroy(mutex),
			MUTEX_SUCCESS);
	thread_test("destroying uninitialized mutex", uthread_mutex_destroy(mutex),
			MUTEX_UNINITIALIZED);
	thread_test("destroying NULL mutex", uthread_mutex_destroy(NULL),
			MUTEX_INVALID);
	return 0;
}
int thread1(void* arg) {
	uthread_mutex_t mutex = (uthread_mutex_t) arg; // alread initialized
	thread_print("a.entered thread1");
	thread_print("b.trying to get mutex");
	if (uthread_mutex_try_lock(mutex) != MUTEX_LOCKED) {
		thread_print("c.had got mutex");
		thread_test("d.so let's unlock it", uthread_mutex_unlock(mutex),
				MUTEX_SUCCESS);
	}
	thread_print("e.before getting mutex");
	thread_test("got mutex", uthread_mutex_lock(mutex), MUTEX_SUCCESS);
	thread_test("f.let's kill next thread", uthread_cancel(uthread_self() + 1),
			UTHREAD_SUCCESS);
	thread_print("waiting 1 sec");
	wait_sec(1);
	thread_test("g.locking mutex once more", uthread_mutex_lock(mutex),
			MUTEX_SUCCESS);
	thread_test("h.destroying locked mutex", uthread_mutex_destroy(mutex),
			MUTEX_LOCKED);
	thread_test("i.unlocking mutex", uthread_mutex_unlock(mutex), MUTEX_SUCCESS);
	//	thread_print("after getting mutex");
	thread_test("j.unlocking mutex once more", uthread_mutex_unlock(mutex),
			MUTEX_SUCCESS);
	thread_test("k.unlocking mutex once ONCE more",
			uthread_mutex_unlock(mutex), MUTEX_LOCKED);
	//	 thread_test("destroying locked mutex",
	//		 uthread_mutex_destroy(mutex), MUTEX_LOCKED);
	// thread_test("destroying locked mutex",
	//		 uthread_mutex_destroy(mutex), MUTEX_LOCKED);
	thread_test("destroying NULL mutex", uthread_mutex_destroy(NULL),
			MUTEX_INVALID);
	thread_print("not ready to die yet :-( !!!");

	return 0;
}

int f1(void* arg) {

	thread_test("g.locking mutex1", uthread_mutex_lock(mutex), MUTEX_SUCCESS);

	thread_test("g.locking mutex2", uthread_mutex_lock(mutex2), MUTEX_SUCCESS);

	thread_test("i.unlocking mutex1", uthread_mutex_unlock(mutex),
			MUTEX_SUCCESS);

	thread_test("i.unlocking mutex2", uthread_mutex_unlock(mutex2),
			MUTEX_SUCCESS);

	return 0;
}

int t3(void* arg) {

	thread_test("T3 Joining T2", uthread_join(2, NULL),
			UTHREAD_FAIL);

	return 0;
}

int t2(void* arg) {

	wait_sec(4);

	wait_sec(3);
	uthread_id th3 = uthread_create(t3, (void*) NULL, 1);

	thread_test("T2 CANCELling T1", uthread_cancel(1),
			UTHREAD_SUCCESS);

	wait_sec(5);

	return 0;
}

int tA(void* arg);
int tB(void* arg);
int tC(void* arg);
int tD(void* arg);

int t1(void* arg) {

	uthread_id th2 = uthread_create(t2, (void*)NULL, 5);

	printf("T1 Joining T2\n");
	uthread_join(th2, NULL);

	return 0;
}

int tA(void* arg) {

	uthread_id th2 = uthread_create(tB, (void*)NULL, 5);

	printf("A Joining B\n",th2);
	thread_test("A Joining B",uthread_join(th2, NULL),UTHREAD_SUCCESS);
	wait_sec(1);
	return 0;
}
int tB(void* arg) {

	uthread_id th2 = uthread_create(tC, (void*)NULL, 5);
	printf("B Joining C\n");
	thread_test("B Joining C",uthread_join(th2, NULL),UTHREAD_SUCCESS);
	wait_sec(1);
	return 0;
}

int tC(void* arg) {
	thread_test("C cancelling B",uthread_cancel(2),UTHREAD_SUCCESS);
	uthread_id th2 = uthread_create(tD, (void*)NULL, 5);

	wait_sec(5);

	return 0;
}
int tD(void* arg) {

	//printf("D Joining C\n");
	thread_test("D Joining C",uthread_join(3, NULL),UTHREAD_FAIL);
	return 0;
}

int main() {
	int i;
	// thread_print("created");
	int threads = 2;

	uthread_init();
//	/********************** Test 1 ***********************/
//	thread_test("creating mutex", uthread_mutex_init(&mutex), MUTEX_SUCCESS);
//	int k = 2;
//	for (i = 0; i < threads; i++) {
//		k++;
//		printf("creating thread %d\n",
//				uthread_create(thread1, (void*) mutex, 1));
//	}
//
//	for (i = 1; i <= threads; i++)
//		uthread_join(i, NULL);
//
//	thread_test("destroying unlocked mutex", uthread_mutex_destroy(mutex),
//			MUTEX_SUCCESS);
//
//	printf("END OF TEST 1\n");
//	/********************** END ***************************/
//
//	/********************** Test 2 ***********************/
//	thread_test("creating mutex", uthread_mutex_init(&mutex), MUTEX_SUCCESS);
//
//	thread_test("creating mutex2", uthread_mutex_init(&mutex2), MUTEX_SUCCESS);
//
//	uthread_id th2 = uthread_create(f1, (void*) NULL, 1);
//
//	thread_test("Creating thread number 2", th2, 1);
//
//	uthread_join(th2, NULL);
//
//	thread_test("destroying unlocked mutex", uthread_mutex_destroy(mutex),
//			MUTEX_SUCCESS);
//
//	thread_test("destroying unlocked mutex2", uthread_mutex_destroy(mutex2),
//			MUTEX_SUCCESS);
//
//	printf("END OF TEST 2\n");
//	/********************** END ***************************/
////
////	/********************** TEST 3 **************************/
////
////	uthread_id th1 = uthread_create(t1, (void*) NULL, 1);
////
////	uthread_join(th1, NULL);
////	uthread_join(4,NULL);
////
////	printf("END OF TEST 3\n");
////	/********************** END ***************************/
//
////	/********************** TEST 4 **************************/
////	printf("creating thread A\n",
////					uthread_create(tA, (void*) mutex, 1));
////
////	uthread_join(1,NULL);

	thread_stats();
	return 0;
}
